#!/usr/bin/env python
#coding=utf8

"""
Name:   iWeather
Date:   2007-04-11
Author: Xupeng Yun <recordus AT gmail.com>
Brief:  This is a gnome applet for weather forecasting, which grab weather
        information from www.tq121.com.
"""

import os
import re
import sys
import time
import socket
import shelve
import urllib
import gobject
import BeautifulSoup
import gnomeapplet as applet

import pygtk
pygtk.require('2.0')
import gtk

IS_SHOWING_DETAIL = False

class iWeather:
    def __init__(self, applet, iid):
        self.applet = applet
        self.menu_str = """ 
            <popup name="button3">
              <menuitem name="About" verb="About" _label="关于(_A)"/>
              <menuitem name="Update" verb="Update" _label="更新(_U)"/>
              <menuitem name="Detail" verb="Detail" _label="详细(_D)"/>
            </popup>
        """
        self.applet.setup_menu(self.menu_str, 
                [('Update', self.update_now), ('About', self.on_about), ('Detail', self.show_detail)], 
                None)
        conf = self.get_config()
        cities = conf[0]
        interval = conf[1]
        self.label = gtk.Label()
        self.label.show()
        self.eb = gtk.EventBox()
        self.eb.add(self.label)
        self.eb.show()
        self.tips = gtk.Tooltips()
        self.show_last_info()
        gobject.timeout_add(interval, self.update)
        gobject.timeout_add(2000, self.update, True)

        self.applet.add(self.eb)
        applet.show_all()

    def update_now(self, event, data=None):
        self.label.set_text(u'正在更新...')
        self.tips.set_tip(self.eb, u'正在更新...')
        self.update()

    def on_about(self, event, data=None):
        about = gtk.AboutDialog()
        about.set_name('iWeather')
        about.set_version('1.0')
        about.set_copyright('(C)2007 Xupeng Yun Copyleft')
        about.set_authors(['Xupeng Yun <recordus AT gmail.com>'])
        about.set_website('http://recordus.cublog.cn')
        about.set_website_label('关注作者')

        about.run()
        about.destroy()

    def check_db(self):
        confdir = os.path.expanduser('~/.iweather')
        if not os.path.exists(confdir):
            os.mkdir(confdir)
        dbpath = os.path.expanduser('~/.iweather/iweather.bdb')
        try:
            db = shelve.open(dbpath)
            db.close()
        except:
            os.remove(dbpath)
        return dbpath

    def save_weather_info(self, info_dict):
        dbpath = self.check_db()
        db = shelve.open(dbpath)
        key = info_dict['city'].encode('utf8', 'ignore')
        info_dict_old = db.get(key, None)
        if info_dict != info_dict_old:
            db[key] = info_dict
        db.close()

    def load_weather_info(self, city):
        dbpath = self.check_db()
        db = shelve.open(dbpath)
        key = city.encode('utf8', 'ignore')
        info_dict = db.get(key, None)
        db.close()

        return info_dict

    def show_last_info(self):
        # use bool var first to show first city's information only.
        first = True
        tips_string = ''
        cities = self.get_config()[0]
        for city in cities:
            status = self.load_weather_info(city)
            if status:
                today = re.findall(u'(-?\d+\u2103)', status['today'].decode('utf8'))
                today.sort()
                today = ' / '.join(today)
                if first:
                    self.label.set_text(today)
                    first = False
                tips_string += '\n' + status['city'] + '：\n' + status['update_time'] + '\n' + status['alldays'] + "\n"
            else:
                if first:
                    self.label.set_text('正在更新…')
                    first = False
                tips_string += '\n' + city + '：\n正在更新…\n'
            self.tips.set_tip(self.eb, tips_string.strip())

        return True

    def get_weather_info(self, city):
        # check if network is ok first, because if the link is down, the applet will be freezing for long time
        # as socket timeout doesn't apply to domainname resolving.
        if os.system('ping 192.168.1.1 -c 1 > /dev/null 2>&1') != 0 and \
            os.system('ping 192.168.1.100 -c 1 > /dev/null 2>&1') != 0:
            return
        url = 'http://weather.tq121.com.cn/detail.php?city=%s' % urllib.quote(city.encode('gbk'))
        socket.setdefaulttimeout(10)
        try:
            html = urllib.urlopen(url).read().decode('gbk', 'ignore')
        except:
            return
        #html = open('/home/aiyi/workspace/weather/shijiazhuang.html').read().decode('gbk', 'ignore')
        main_soup = BeautifulSoup.BeautifulSoup(html)
        # Matches strings like 7月10日
        _dates = main_soup.findAll(text=re.compile(u'\d+\u6708\d+\u65e5'))
        _alldays = ''
        _today = ''
        for _date in _dates:
            _aday_table = _date.findParents('table', limit=2)[-1]
            _aday_img = _aday_table.findAll('img')
            _aday_img = [a['src'] for a in _aday_img]
            _aday_text = _aday_table.findAll(text=lambda x: x.replace('&nbsp;', '',).strip())
            _aday_text = [a.strip().ljust(9) for a in _aday_text]
            if _date == _dates[0]:
                _today = '\t'.join(_aday_text)
            if _alldays:
                _alldays += '\n'
            _alldays += '\t'.join(_aday_text)
            #_alldays += '\n' + str(_aday_img)
        _search_result = main_soup.findAll(text=re.compile('\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}'))
        if _search_result:
            update_time = _search_result[0]
        else:
            update_time = u'预报发布时间：未知'

        # fetch details
        res = main_soup.findAll(text=re.compile(u'\u6307\u6570$'))
        detail_str = ''
        for name in res:
            detail_str += name.strip() + '\n'
            brief = name.findNext()
            if brief:
                if brief.string:
                    detail_str += brief.string.strip() + '\n'
                else:
                    detail_str += u'未知' + '\n'
                detail = brief.findNext()
                if detail:
                    if detail.string:
                        detail_str += detail.string.strip() + '\n\n'
                    else:
                        detail_str += u'未知' + '\n\n'
        detail_str = detail_str.strip()

        info_dict = {'city' : city.encode('utf8'),
                     'update_time' : update_time.encode('utf8'),
                     'today'        : _today.encode('utf8'),
                     'alldays'      : _alldays.encode('utf8'),
                     'details'      : detail_str.encode('utf8')
                     }

        self.save_weather_info(info_dict)

    def update(self, once=False):
        first = True
        detail = ''
        cities = self.get_config()[0]
        for city in cities:
            self.get_weather_info(city)
        self.show_last_info()
        return not once

    def show_detail(self, event, data=None):
        global IS_SHOWING_DETAIL
        def close_dialog(event):
            global IS_SHOWING_DETAIL
            IS_SHOWING_DETAIL = False
        if not IS_SHOWING_DETAIL:
            dlg = gtk.Dialog(u'详细信息', flags=gtk.DIALOG_MODAL|gtk.DIALOG_NO_SEPARATOR)
            dlg.connect('destroy', close_dialog)
            dlg.set_size_request(600,400)
            nb = gtk.Notebook()
            cities = self.get_config()[0]

            for city in cities:
                nb_label = gtk.Label(city)
                nb_label.show()

                txt_view = gtk.TextView()
                txt_view.set_wrap_mode(gtk.WRAP_WORD)
                txt_view.show()
                txt_view.set_editable(False)
                txt_view.set_cursor_visible(False)
                txt_buffer = txt_view.get_buffer()

                status = self.load_weather_info(city)
                if status:
                    detail = status['details']
                    detail_str = ''
                    exps = detail.split('\n\n')
                    for exp in exps:
                        items = exp.split()
                        if len(items) < 3:
                            continue
                        detail_str += items[0] + '：' + items[1] + '\n' + items[2] + '\n\n'
                    txt_buffer.set_text(detail_str.strip())
                else:
                    txt_buffer.set_text('正在更新...')

                sw = gtk.ScrolledWindow()
                sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                sw.add(txt_view)
                sw.show()
                nb.append_page(sw, nb_label)

            nb.show()
            dlg.vbox.pack_start(nb)
            dlg.show()
            IS_SHOWING_DETAIL = True
            return True
        else:
            return False

    def get_config(self):
        confdir = os.path.expanduser('~/.iweather/')
        conffile = os.path.expanduser('~/.iweather/iweather.conf')
        conf = {}
        if os.path.exists(conffile):
            lines = open(conffile).readlines()
            for line in lines:
                if not line.strip().startswith('#'):
                    try:
                        a, b = line.split('=')
                        conf[a.strip()] = b.strip()
                    except ValueError:
                        continue

        cities = [c.strip().decode('utf8', 'ignore') \
                for c in conf.get('cities', '北京').split(',')]
        interval_str = conf.get('interval', '1h').strip()
        unit = interval_str[-1].lower()
        try:
            interval = int(interval_str[:-1])
            if interval <= 0:
                interval = 1
        except:
            interval = 1
        if unit == 'h':
            interval *= 3600*1000 
        elif unit == 'm':
            interval *= 60*1000 
        elif unit == 's':
            interval *= 1000 
        else:
            interval = 3600*1000

        return [cities, interval]

def iweather_factory(applet, iid):
    iWeather(applet, iid)
    return True

def main():
    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
    app = applet.Applet()
    iweather_factory(app, None)
    app.reparent(win)
    win.show_all()
    gtk.main()
    sys.exit()

if len(sys.argv) > 1 and sys.argv[1] == 'win':
    main()
else:
    applet.bonobo_factory("OAFIID:GNOME_iWeatherApplet_Factory", 
            applet.Applet.__gtype__, "iWeather", "0", 
            iweather_factory)
